class InputSpecifier():
    def __init__(self):
        import re
        self.ifilter1=[]
        # 0. structure
        self.ifilter1.append(re.compile(r"struct\s+(\S+)"))
        # 1. lattice_param
        self.ifilter1.append(re.compile(r"lattice_param\s+(\S+)"))
        # 2. ignore_layer
        self.ifilter1.append(re.compile(r"ignore_layer\s+(\S+)"))
        # 3. file name from MD
        self.ifilter1.append(re.compile(r"file\s+(\S+)"))
        # 4. file name for xmakemol
        self.ifilter1.append(re.compile(r"xmakemol\s+(\S+)"))
        # 5. file name for kmc
        self.ifilter1.append(re.compile(r"kmcfile\s+(\S+)"))
        # 6. file name for kmc
        self.ifilter1.append(re.compile(r"case\s+"))

    def if1gen(self):
        for item in self.ifilter1:
            yield item

def iround(x):
    return int(round(x) - 0.5)+(x>0)

class InputParam():
    def __init__(self):
        import BasicDataType as type1
        # Input specifier
        self.struct=""
        self.lattice_param=0.0
        self.ignore_layer=0
        self.file=""
        self.xmakemol=""
        self.kmcname=""
        self.case=""
        # Basis info: Normalized/Actual position
        self.basis=type1.cellbase()
        self.basis2=type1.cellbase()
        # List of file name
        self.file_number_list=[]
        self.srcfile=[]
        self.xmlfile=[]
        self.kmcfile=[]
        # Number of atoms and related parameters
        self.natom=0
        self.ncell_in_plane=0
        self.ncell_in_line=0
        self.lencell=0
        self.ncell=0
        self.boxwidth=0.0
        # Extra atoms involed with output
        self.xmlframe=[]
        self.remove_layer1=[]
        self.remove_layer2=[]
        # Save counting information
        self.unitcell=[]
        # self.defect=[]
        # Defect counter
        self.nnormal=0
        self.nSIA=0
        self.nvacancy=0
        self.nodd=0
        self.n_defect_atom=0
        self.SIA_list=[]
        self.vacancy_list=[]
        self.odd_list=[]

    def set_case_string(self,s):
        self.case=s.strip()
        return self

    def get_case_string(self):
        return self.case

    def add_SIA_list(self,n1,n2):
        self.SIA_list.append([n1,n2])

    def add_vacancy_list(self,n1,n2):
        self.vacancy_list.append([n1,n2])

    def add_odd_list(self,n1,n2):
        self.odd_list.append([n1,n2])

    def get_SIA_list(self,n):
        return self.SIA_list[n]

    def get_vacancy_list(self,n):
        return self.vacancy_list[n]

    def get_odd_list(self,n):
        return self.odd_list[n]

    def count_vacancy(self):
        self.nvacancy+=1

    def count_SIA(self):
        self.nSIA+=1

    def count_normal(self):
        self.nnormal+=1

    def count_odd(self):
        self.nodd+=1

    def get_nvacancy(self):
        return self.nvacancy

    def get_nSIA(self):
        return self.nSIA

    def get_nnormal(self):
        return self.nnormal

    def get_nodd(self):
        return self.nodd

    def get_n_defect_atom(self):
        return self.n_defect_atom

    def set_unitcell(self):
        import BasicDataType as type1
        # Create lattice point to save defects
        for i in range(self.get_ncell()):
            self.unitcell.append(type1.basis())
            # self.unitcell[i].add_basepoint(self.get_nbasis())
            self.unitcell[i].set_atomlist_in_unitcell(self.get_nbasis())
        return self

    def get_kmcfile(self,n):
        return self.kmcfile[n]

    def set_rlayer(self,n):
        self.remove_layer1.append(n)
        self.remove_layer2.append(self.lencell-n-1)
        return self

    def get_boxwidth(self):
        return self.boxwidth

    def get_ncell(self):
        return self.ncell

    def get_lencell(self):
        return self.lencell

    def set_natom(self,n):
        import math
        import BasicDataType as type1

        # Set lattice information
        self.natom=n

        # Determine lattice parameters
        if self.struct=="bcc":
            self.ncell=iround(0.5*n)
            self.lencell=iround(math.pow(float(self.ncell),1.0/3.0))
            self.boxwidth=float(self.lencell)*self.lattice_param
        elif self.struct=="fcc":
            self.ncell=iround(0.25*n)
            self.lencell=iround(math.pow(float(self.ncell),1.0/3.0))
            self.boxwidth=float(self.lencell)*self.lattice_param
        else:
            print("Struct types %s are not ready." % self.struct)
            raise Exception

        # Set xmakemol structure atoms
        basepoint=[0.0,self.boxwidth+1.0]
        for i in range(2):
            for j in range(2):
                for k in range(2):
                    x=basepoint[i]
                    y=basepoint[j]
                    z=basepoint[k]
                    self.xmlframe.append(type1.Points(x,y,z))                    

        # Cell parameter
        self.ncell_in_plane=iround(self.lencell**2)
        self.ncell_in_line=iround(self.lencell)

        return self

    def gen_xmlframe(self):
        for item in self.xmlframe:
            yield item.get_pos()

    def get_nxmlframe(self):
        return len(self.xmlframe)

    def get_ncell_in_plane(self):
        return self.ncell_in_plane

    def get_ncell_in_line(self):
        return self.ncell_in_line

    def count_nearest_point(self,ix,iy,iz,px,py,pz,bn):
        unit_cell_no=ix\
            +iy*self.ncell_in_line\
            +iz*self.ncell_in_plane
        # self.unitcell[unit_cell_no].count_basepoint(bn)
        self.unitcell[unit_cell_no].add_atom(bn,px,py,pz)
        return self

    def reset_count(self):
        for i in range(self.get_ncell()):
            for j in range(self.get_nbasis()):
                # self.unitcell[i].set_basepoint(j,0)
                self.unitcell[i].init_atomlist_in_unitcell(j)
        self.nnormal=0
        self.nSIA=0
        self.nvacancy=0
        self.nodd=0
        self.n_defect_atom=0
        self.SIA_list=[]
        self.vacancy_list=[]
        self.odd_list=[]
        return self

    # def get_ndefect(self):
    #     return len(self.defect)

    # def gen_defect(self):
    #     for item in self.defect:
    #         yield item.getx(),item.gety(),item.getz(),item.get_defect_type()

    def get_natom(self):
        return self.natom

    def add_xmlfile(self,s):
        self.xmlfile.append(s.strip())
        return self

    def add_kmcfile(self,s):
        self.kmcfile.append(s.strip())
        return self

    def get_kmcfile(self,n):
        return self.kmcfile[n]

    def get_nkmcfile(self):
        return len(self.kmcfile)

    def get_nxmlfile(self):
        return len(self.xmlfile)

    def get_xmlfile(self,n):
        return self.xmlfile[n]

    def add_srcfile(self,s):
        self.srcfile.append(s.strip())
        return self

    def get_nsrcfile(self):
        return len(self.srcfile)

    def get_srcfile(self,n):
        return self.srcfile[n]

    def add_basis(self,x,y,z):
        self.basis.set_base(x,y,z)
        return self

    def add_basis2(self,x,y,z):
        self.basis2.set_base(x,y,z)
        return self

    def get_basis2_pos(self,n):
        return self.basis2.get_pos(n)

    def get_nbasis(self):
        return self.basis.get_nbase()

    def get_basis_pos(self,n):
        return self.basis.get_pos(n)

    def set_struct(self,s):
        self.struct=s.strip()
        return self

    def set_lattice_param(self,f):
        self.lattice_param=f
        return self

    def set_ignore_layer(self,n):
        self.ignore_layer=n
        return self

    def set_source_file(self,s):
        self.file=s.strip()
        return self

    def set_xmakemol(self,s):
        self.xmakemol=s.strip()
        return self

    def set_kmcname(self,s):
        self.kmcname=s.strip()
        return self

    def get_kmcname(self):
        return self.kmcname

    def get_struct(self):
        return self.struct

    def get_lattice_param(self):
        return self.lattice_param

    def get_ignore_layer(self):
        return self.ignore_layer

    def get_source_file(self):
        return self.file

    def get_xmakemol(self):
        return self.xmakemol

    def neighboriter(self,ix,iy,iz):
        for i in range(-1,2,1):
            for j in range(-1,2,1):
                for k in range(-1,2,1):
                    lx=ix+i
                    ly=iy+j
                    lz=iz+k
                    yield lx,ly,lz

    def gen_lattice_point_in_cell(self,cell_info):
        lx=cell_info[0]
        ly=cell_info[1]
        lz=cell_info[2]
        if lx<0:
            lx=lx+self.get_lencell()
            boundx=-1.0
        elif lx>=self.get_lencell():
            lx=lx-self.get_lencell()
            boundx=1.0
        else:
            boundx=0
        if ly<0:
            ly=ly+self.get_lencell()
            boundy=-1.0
        elif ly>=self.get_lencell():
            ly=ly-self.get_lencell()
            boundy=1.0
        else:
            boundy=0
        if lz<0:
            lz=lz+self.get_lencell()
            boundz=-1.0
        elif lz>=self.get_lencell():
            lz=lz-self.get_lencell()
            boundz=1.0
        else:
            boundz=0
        px=float(lx)*self.get_lattice_param()
        py=float(ly)*self.get_lattice_param()
        pz=float(lz)*self.get_lattice_param()
        for i in range(self.get_nbasis()):
            x=px+self.get_basis2_pos(i)[0]+boundx*self.get_boxwidth()
            y=py+self.get_basis2_pos(i)[1]+boundy*self.get_boxwidth()
            z=pz+self.get_basis2_pos(i)[2]+boundz*self.get_boxwidth()
            yield x,y,z,lx,ly,lz,i

iparam=InputParam()
